package draw;

import histogram.HistogramDescr;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Stack;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;

public class DrawFilter implements MouseMotionListener, MouseListener {

    Color edge = new Color(ImageFromArray.mitColor[0], ImageFromArray.mitColor[1],
            ImageFromArray.mitColor[2], 255);
    Stack<int[][]> binImageStack = new Stack<int[][]>();
    Stack<int[][]> neiroMarkedStack = new Stack<int[][]>();
    private BufferedImage image;
    private DrawPanel parentPanel;
    private int[][] binImage;
    private int[][] neiroMark;
    private int origin[][];
    private boolean isDragged = false;

    public DrawFilter(int[][] origin, BufferedImage image, DrawPanel parentPanel, int[][] binImage, int[][] neiroMark) {
        this.image = image;
        this.parentPanel = parentPanel;
        this.binImage = binImage;
        this.neiroMark = neiroMark;
        this.origin = origin;
    }
    int lastX = -1;
    int lastY = -1;
    int curX = -1;
    int curY = -1;
    int startx = -1;
    int starty = -1;

    @Override
    public void mouseDragged(MouseEvent me) {
        curX = me.getX();
        curY = me.getY();
        Graphics2D g2 = (Graphics2D) image.getGraphics();
        g2.setStroke(new BasicStroke(2.0f));
        g2.setColor(edge);
        g2.drawLine(lastX, lastY, curX, curY);
        parentPanel.repaint();
        lastX = me.getX();
        lastY = me.getY();

        isDragged = true;
    }

    @Override
    public void mouseMoved(MouseEvent me) {
    }

    @Override
    public void mouseClicked(MouseEvent me) {
        if (me.getModifiers() == MouseEvent.BUTTON3_MASK) {
            doPop(me);
        }
    }

    @Override
    public void mousePressed(MouseEvent me) {
        lastX = me.getX();
        lastY = me.getY();
        curX = me.getX();
        curY = me.getY();

        startx = curX;
        starty = curY;
    }

    @Override
    public void mouseReleased(MouseEvent me) {

        if (isDragged) {
            saveCurState();
        }
        isDragged = false;

        updateOriginImage(startx, starty);
    }

    @Override
    public void mouseEntered(MouseEvent me) {
    }

    @Override
    public void mouseExited(MouseEvent me) {
    }

    private void doPop(MouseEvent e) {
        PopUpDemo menu = new PopUpDemo(e.getX(), e.getY());
        menu.show(e.getComponent(), e.getX(), e.getY());
        menu.setVisible(true);
    }

    class PopUpDemo extends JPopupMenu {

        JMenuItem anItem;
        JMenuItem undo;

        public PopUpDemo(final int x, final int y) {

            anItem = new JMenuItem("Delete region");
            anItem.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent ae) {
//                    System.out.println("delete region");
                    
                    toBackground(x, y);
                    BufferedImage im = ImageFromArray.createColorImageFromArray(origin, binImage, neiroMark);
                    parentPanel.setSrcImage(im);
                    image = im;
                    parentPanel.repaint();
                }
            });

            add(anItem);

            undo = new JMenuItem("Undo");
            undo.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent ae) {
                    
                    if (!neiroMarkedStack.isEmpty()) {
                        neiroMark = neiroMarkedStack.pop();

                        binImage = binImageStack.pop();
                    }

                    BufferedImage im = ImageFromArray.createColorImageFromArray(origin, binImage, neiroMark);
                    parentPanel.setSrcImage(im);
                    image = im;
                    parentPanel.repaint();
                }
            });

            add(undo);
        }
    }
    private static final int TEMP_MARK = 371;

    private void saveCurState() {
        int width = image.getWidth();
        int height = image.getHeight();

        int oldNeiro[][] = new int[width][height];
        int oldBinIm[][] = new int[width][height];

        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {
                oldNeiro[i][j] = neiroMark[i][j];
                oldBinIm[i][j] = binImage[i][j];
            }
        }

        binImageStack.push(oldBinIm);
        neiroMarkedStack.push(oldNeiro);
    }

    private void updateOriginImage(int endX, int endY) {
        mark(endX, endY, false);
        mark(endX, endY, true);
    }

    /**
     * @param howPaint 
     * true if paint with tempColor
     * false if repaint in from temp to mitach
     */
    private void mark(int endX, int endY, final boolean howPaint) {
        LinkedList<Integer> xQueue = new LinkedList<Integer>();
        LinkedList<Integer> yQueue = new LinkedList<Integer>();

        int width = image.getWidth();
        int height = image.getHeight();

        Raster rast = image.getRaster();

        xQueue.push(endX);
        yQueue.push(endY);

        int dx[] = {-1, 0, 1};

        int curColor[] = new int[4];
        int tempColor[] = new int[4];
        int count = 0;

        while (!xQueue.isEmpty()) {
            int x = xQueue.pop();
            int y = yQueue.pop();


            if (x >= width || y >= height || x < 0 || y < 0) {
                continue;
            }

            rast.getPixel(x, y, curColor);

            if (Arrays.equals(curColor, ImageFromArray.mitColor)
                    && ((howPaint && neiroMark[x][y] != HistogramDescr.MARK_EDGE)
                    || (!howPaint && neiroMark[x][y] != TEMP_MARK))) {
                boolean middle = true;

//                for (int i = x - 1; i <= x + 1; i++) {
//                    for (int j = y - 1; j <= y + 1; j++) {
//
//                        if (i >= width || j >= height || i < 0 || j < 0) {
//                            continue;
//                        }
//
//                        rast.getPixel(i, j, tempColor);
//
//                        if (!Arrays.equals(tempColor, ImageFromArray.mitColor)) {
//                            middle = false;
//                        }
//
//                    }
//                }

                if (middle) {
                    if (!howPaint) {
                        neiroMark[x][y] = TEMP_MARK;
                    } else {
                        neiroMark[x][y] = HistogramDescr.MARK_EDGE;
                    }
                    count++;
                }

                for (int i = 0; i < 3; i++) {
                    for (int j = 0; j < 3; j++) {
                        xQueue.push(x + dx[i]);
                        yQueue.push(y + dx[j]);
                    }
                }
            }
        }


    }

    private void toBackground(int xPoint, int yPoint) {
        int TYPE = neiroMark[xPoint][yPoint];
//        System.out.println(TYPE);
        if (TYPE == HistogramDescr.BACK) {
            return;
        }
        saveCurState();
        
        LinkedList<Integer> xQueue = new LinkedList<Integer>();
        LinkedList<Integer> yQueue = new LinkedList<Integer>();

        int width = image.getWidth();
        int height = image.getHeight();

        xQueue.push(xPoint);
        yQueue.push(yPoint);
        int count = 0;

        int dx[] = {-1, 0, 1};

        while (!xQueue.isEmpty()) {
            int x = xQueue.pop();
            int y = yQueue.pop();

            if (x >= width || y >= height || x < 0 || y < 0) {
                continue;
            }

            if (neiroMark[x][y] == TYPE) {
                neiroMark[x][y] = HistogramDescr.BACK;
                binImage[x][y] = 0;
                count++;
                for (int i = 0; i < 3; i++) {
                    for (int j = 0; j < 3; j++) {
                        xQueue.push(x + dx[i]);
                        yQueue.push(y + dx[j]);
                    }
                }
            }
        }

    }
}
